package org.zjt.lock;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.exec.*;

import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author juntao.zhang
 * @version V1.0
 * @Description: TODO
 * @Package org.zjt.lock
 * @date 2018/6/22 23:42
 */
@Slf4j
public class DeadLock {

    private static final String LOCK_A = "lock_a";
    private static final String LOCK_B = "lock_b";


    public static void method1() throws InterruptedException {

        synchronized (LOCK_A){
            TimeUnit.SECONDS.sleep(5);
            synchronized (LOCK_B){
                log.info("----------------running method1-----------------");
            }
        }
    }


    public static void method2() throws InterruptedException {
        synchronized (LOCK_B){
            TimeUnit.SECONDS.sleep(5);
            synchronized (LOCK_A){
                log.info("----------------running method2-----------------");
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            try {
                method1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                method2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();


        DeadlockDetector deadlockDetector = new DeadlockDetector(new MyDeadlockHandler(), 2, TimeUnit.SECONDS);
        deadlockDetector.start();


        log.info("-------------------------------");

        TimeUnit.SECONDS.sleep(40);
    }



    public static class CMD {
        public static void main(String[] args) throws IOException, InterruptedException {
            CommandLine cmdLine = new CommandLine("AcroRd32.exe");
            cmdLine.addArgument("/p");
            cmdLine.addArgument("/h");
            cmdLine.addArgument("${file}");
            HashMap map = new HashMap();
            map.put("file", new File("invoice.pdf"));
            cmdLine.setSubstitutionMap(map);

            DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();

            ExecuteWatchdog watchdog = new ExecuteWatchdog(60*1000);
            Executor executor = new DefaultExecutor();
            executor.setExitValue(1);
            executor.setWatchdog(watchdog);
            executor.execute(cmdLine, resultHandler);

            // some time later the result handler callback was invoked so we
            // can safely request the exit value
             resultHandler.waitFor();
        }
    }




    public interface DeadlockHandler {
        void handleDeadlock(final ThreadInfo[] deadlockedThreads);
    }

    @Slf4j
    public static class MyDeadlockHandler implements DeadlockHandler{

        @Override
        public void handleDeadlock(ThreadInfo[] deadlockedThreads) {
            Arrays.stream(deadlockedThreads)
                    .map(item ->String.format("LockName:%s \t ThreadId:%s\tThreadName :%s \tThreadState :%s", item.getLockName(),item.getThreadId(), item.getThreadName(),item.getThreadState()))
                    .forEach(log::error);
        }
    }

    /**
     * 检测死锁
     */
    public static class DeadlockDetector {

        private final DeadlockHandler deadlockHandler;
        private final long period;
        private final TimeUnit unit;
        private final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
        private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        final Runnable deadlockCheck = () -> {
            long[] deadlockedThreadIds = DeadlockDetector.this.mbean.findDeadlockedThreads();
            if (deadlockedThreadIds != null) {
                ThreadInfo[] threadInfos = DeadlockDetector.this.mbean.getThreadInfo(deadlockedThreadIds);
                DeadlockDetector.this.deadlockHandler.handleDeadlock(threadInfos);
            }
        };

        public DeadlockDetector(final DeadlockHandler deadlockHandler, final long period, final TimeUnit unit) {
            this.deadlockHandler = deadlockHandler;
            this.period = period;
            this.unit = unit;
        }

        public void start() {
            this.scheduler.scheduleAtFixedRate(this.deadlockCheck, this.period, this.period, this.unit);
        }
    }


}
